package org.nwiz.calc;

import com.sun.istack.internal.Nullable;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;

import javax.swing.*;

public class Lexer {

    public enum TokenType {
        ADD, SUB, MUL, DIV, NUMBER, ERROR, LP, RP;

        private static final char names[] = {'+', '-', '*', '/', '(', ')'};

        private static final TokenType values[] = {ADD, SUB, MUL, DIV, LP, RP};

        public static TokenType valueOf(char sym) {
            for (int i = 0; i < names.length; i++) {
                if (names[i] == sym)
                    return values[i];
            }
            return ERROR;
        }

        @Override
        public String toString() {
            for (int i = 0; i < names.length; i++) {
                if (values[i] == this)
                    return String.valueOf(names[i]);
            }
            if (this == ERROR)
                return "<ERROR>";
            return "";
        }
    }

    public static class Token {
        private TokenType type;
        private double value;

        public Token(char sym) {
            this.type = TokenType.valueOf(sym);
        }

        public Token(double value) {
            this.type = TokenType.NUMBER;
            this.value = value;
        }

        public boolean isNumber() {
            return this.type == TokenType.NUMBER;
        }

        public double getValue() {
            if (this.type == TokenType.NUMBER) {
                return this.value;
            } else {
                return -1;
            }
        }

        public TokenType getType() {
            return this.type;
        }

        @Override
        public String toString() {
            if (this.type == TokenType.NUMBER) {
                return String.valueOf(value);
            } else {
                return this.type.toString();
            }
        }
    }

    public static class CalcLexer {
        private String source;
        private int pos;
        private int limit;
        private char curr;
        private Token last;
        private boolean lock;

        public CalcLexer(String source) {
            this.source = source;
            this.pos = 0;
            this.curr = this.source.charAt(0);
            this.limit = source.length();
            this.lock = false;
        }

        private Token readNumber() {
            int right = this.pos;
            int i;
            for (i = right; i < this.limit; i++) {
                char c = this.source.charAt(i);
                if (!Character.isDigit(c) && c != '.') {
                    right = i;
                    break;
                }
            }
            if (i == this.limit)
                right = this.limit;
            double value = Double.valueOf(this.source.substring(this.pos, right));
            this.pos = right;
            return new Token(value);
        }

        private Token readToken() {
            Token tk = new Token(this.curr);
            this.pos += 1;
            if (tk.type == TokenType.ERROR) {
                return null;
            } else {
                return tk;
            }
        }

        public void setLock() {
            this.lock = true;
        }

        public void clearLock() {
            this.lock = false;
        }

        @Nullable
        public Token lex() {
            if (this.lock) {
                this.lock = false;
                return this.last;
            }
            while (true) {
                if (this.pos >= this.limit)
                    return null;
                this.curr = this.source.charAt(this.pos);
                if (Character.isSpaceChar(curr)) {
                    this.pos += 1;
                } else if (Character.isDigit(curr)) {
                    this.last = readNumber();
                    return this.last;
                } else {
                    this.last = readToken();
                    return this.last;
                }
            }
        }

    }
}
